#ifndef __LAMBDA_H__
#define __LAMBDA_H__

typedef void (*app_func_t)(void *arg);
void app_post(app_func_t fn, void *arg);
void* app_post_delay(app_func_t fn, void *arg, uint32_t delay);
void app_cancel_delay(void *handle);

#define _MAKE_NAME(func, line) func##_##line
#define MAKE_NAME(func, line) _MAKE_NAME(func, line)

//do not use -O0, or it will cause error. do not use local variable as parameter
#define APP_RUN(x) ({\
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *arg) { x; }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), NULL);\
})

//do not use -O0, or it will cause error. do not use local variable as parameter
#define APP_RUN_EXT(x, param) ({\
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *_arg) {typeof(param) param=_arg; x; }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), param);\
})

//do not use -O0, or it will cause error. do not use local variable as parameter
#define APP_RUN_DELAY(x, delay) ({\
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *arg) { x; }\
    app_post_delay(MAKE_NAME(__FUNCTION__, __LINE__), NULL, delay);\
})

//do not use -O0, or it will cause error. do not use local variable as parameter
#define APP_RUN_DELAY_EXT(x, param, delay) ({\
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *_arg) {typeof(param) param=_arg; x; }\
    app_post_delay(MAKE_NAME(__FUNCTION__, __LINE__), param, delay);\
})

#define APP_CANCEL_DELAY(handle) app_cancel_delay(handle)

#define get_param_id(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, ...)  a17
#define get_param_num(...) get_param_id(a0, ##__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_ptr_or_array(p) (__builtin_classify_type(p) == 5)
#define decay(a) (&*__builtin_choose_expr(is_ptr_or_array(a), a, NULL))
#define is_pointer(p)      is_same_type(p, decay(p))

#define PARAM_TYPE(x) typeof(*__builtin_choose_expr(is_ptr_or_array(x), x, &x))
#define PARAM_COPY(x) __builtin_choose_expr(is_ptr_or_array(x), x, &x)
#define PARAM_USE(x,v) __builtin_choose_expr(is_ptr_or_array(x), &v, v)

#define APP_POST_0(f) ({\
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *arg) { f(); }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), NULL);\
})

#define APP_POST_1(f, a1) ({\
    typeof(a1) aa1 = a1; \
    struct{ \
        PARAM_TYPE(aa1) a; \
    } *_param = malloc(sizeof(*_param)); \
    memcpy(&_param->a, PARAM_COPY(aa1), sizeof(PARAM_TYPE(aa1))); \
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *_arg) {typeof(_param) param = _arg; f(PARAM_USE(aa1,param->a)); free(param); }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), _param);\
})

#define APP_POST_2(f, a1, a2) ({\
    typeof(a1) aa1 = a1; \
    typeof(a2) aa2 = aa2; \
    struct{ \
        PARAM_TYPE(aa1) a; \
        PARAM_TYPE(aa2) b; \
    } *_param = malloc(sizeof(*_param)); \
    memcpy(&_param->a, PARAM_COPY(aa1), sizeof(PARAM_TYPE(aa1))); \
    memcpy(&_param->b, PARAM_COPY(aa2), sizeof(PARAM_TYPE(aa2))); \
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *_arg) {typeof(_param) param = _arg; f(PARAM_USE(a1,param->a), PARAM_USE(a2,param->b)); free(param); }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), _param);\
})

#define APP_POST_3(f, a1, a2, a3) ({\
    typeof(a1) aa1 = a1; \
    typeof(a2) aa2 = a2; \
    typeof(a3) aa3 = a3; \
    struct{ \
        PARAM_TYPE(aa1) a; \
        PARAM_TYPE(aa2) b; \
        PARAM_TYPE(aa3) c; \
    } *_param = malloc(sizeof(*_param)); \
    memcpy(&_param->a, PARAM_COPY(aa1), sizeof(PARAM_TYPE(aa1))); \
    memcpy(&_param->b, PARAM_COPY(aa2), sizeof(PARAM_TYPE(aa2))); \
    memcpy(&_param->c, PARAM_COPY(aa3), sizeof(PARAM_TYPE(aa3))); \
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *_arg) {typeof(_param) param = _arg; f(PARAM_USE(a1,param->a), PARAM_USE(a2,param->b), PARAM_USE(a3,param->c)); free(param); }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), _param);\
})

#define APP_POST_4(f, a1, a2, a3, a4) ({\
    typeof(a1) aa1 = a1; \
    typeof(a2) aa2 = a2; \
    typeof(a3) aa3 = a3; \
    typeof(a4) aa4 = a4; \
    struct{ \
        PARAM_TYPE(aa1) a; \
        PARAM_TYPE(aa2) b; \
        PARAM_TYPE(aa3) c; \
        PARAM_TYPE(aa4) d; \
    } *_param = malloc(sizeof(*_param)); \
    memcpy(&_param->a, PARAM_COPY(aa1), sizeof(PARAM_TYPE(aa1))); \
    memcpy(&_param->b, PARAM_COPY(aa2), sizeof(PARAM_TYPE(aa2))); \
    memcpy(&_param->c, PARAM_COPY(aa3), sizeof(PARAM_TYPE(aa3))); \
    memcpy(&_param->d, PARAM_COPY(aa4), sizeof(PARAM_TYPE(aa4))); \
    void MAKE_NAME(__FUNCTION__, __LINE__)(void *_arg) {typeof(_param) param = _arg; f(PARAM_USE(a1,param->a), PARAM_USE(a2,param->b), PARAM_USE(a3,param->c), PARAM_USE(a4,param->d)); free(param); }\
    app_post(MAKE_NAME(__FUNCTION__, __LINE__), _param);\
})

//do not use -O0, or it will cause error, do not use array,char* as parameter
#define APP_POST(name, ...) MAKE_NAME(APP_POST,get_param_num(__VA_ARGS__)) (name, ##__VA_ARGS__)



#endif // __LAMBDA_H__